from openai import OpenAI
from typing import Optional, List, Dict
import requests
import json
import os
from dotenv import load_dotenv, find_dotenv
import logging
from llama_index.llms.deepseek import DeepSeek

# 配置日志记录
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    filename='log.log'
)
_ = load_dotenv(find_dotenv(),override=True)


class OpenAIClientWrapper:
    def __init__(
        self,
        base_url: Optional[str] = None,
        api_key: Optional[str] = None,
        model_name: Optional[str] = None
    ):
        """
        初始化LLM客户端，自动调用 update_settings 完成初始化
        """
        self.base_url = None
        self.api_key = None
        self.model_name = None
        self._client = None
        self.model_getway = "openai"
        self.update_settings(base_url, api_key, model_name)  # 通过update_settings统一初始化

    def update_settings(
        self,
        base_url: Optional[str] = None,
        api_key: Optional[str] = None,
        model_name: Optional[str] = None
    ) -> None:
        """更新连接设置并重建客户端"""
        self.base_url = base_url or os.environ['LLM_API_URL']
        self.api_key = api_key or os.environ['LLM_API_KEY']
        self.model_name = model_name or os.environ['LLM_MODEL_NAME']
        self._client = OpenAI(base_url=self.base_url, api_key=self.api_key)  # 重建客户端
        self.model_getway = os.environ['API_MODEL_GETWAY']

        logging.info("模型客户端初始化完成，配置信息如下：")
        logging.info(f" - API URL       : {self.base_url}")
        logging.info(f" - 模型名称       : {self.model_name}")
        logging.info(f" - 发送请求方式       : {self.model_getway}")

    def chat_completion(
        self,
        system_prompt: Optional[str] = None,
        user_input: Optional[str] = None,
        messages: Optional[List[Dict[str, str]]] = None,
        temperature: float = 0.0,
        tools: Optional[List[Dict]] = None,
        stream = False
    ) -> str:
        """
        执行聊天补全（支持自定义messages或传统参数）
        
        参数说明：
        - system_prompt: 系统提示词（与user_input配对使用）
        - user_input: 用户输入内容（与system_prompt配对使用）
        - messages: 完整的消息列表（覆盖system_prompt/user_input模式）
        - temperature: 生成温度
        - tools: 工具调用定义
        
        返回：模型生成的响应内容
        
        示例：
        1. 传统方式：
        chat_completion(system_prompt="你是个助手", user_input="你好")
        2. 自定义messages：
        chat_completion(messages=[{"role":"system", "content":"你是个助手"}, {"role":"user", "content":"你好"}])
        """
        try:
            if self.model_getway == 'openai':
                return self._chat_completion_via_openai(system_prompt=system_prompt,user_input=user_input,messages=messages,temperature=temperature,tools=tools,stream=stream)
            elif self.model_getway =='request':
                return self._chat_completion_via_requests(system_prompt=system_prompt,user_input=user_input,messages=messages,temperature=temperature,tools=tools,stream=stream)
            else:
                raise ValueError(f"不支持的 model_getway 类型: {self.model_getway}")
        except Exception as e:
            logging.error(f"发生错误：{str(e)}")
            return None

    def _chat_completion_via_openai(
        self,
        system_prompt: Optional[str] = None,
        user_input: Optional[str] = None,
        messages: Optional[List[Dict[str, str]]] = None,
        temperature: float = 0.0,
        tools: Optional[List[Dict]] = None,
        stream = False
    ) -> str:
        """
        执行聊天补全（支持自定义messages或传统参数）
        
        参数说明：
        - system_prompt: 系统提示词（与user_input配对使用）
        - user_input: 用户输入内容（与system_prompt配对使用）
        - messages: 完整的消息列表（覆盖system_prompt/user_input模式）
        - temperature: 生成温度
        - tools: 工具调用定义
        
        返回：模型生成的响应内容
        
        示例：
        1. 传统方式：
        chat_completion(system_prompt="你是个助手", user_input="你好")
        2. 自定义messages：
        chat_completion(messages=[{"role":"system", "content":"你是个助手"}, {"role":"user", "content":"你好"}])
        """
        # 参数校验
        if messages is None:
            if not user_input:
                raise ValueError("user_input不能为空或纯空格")
            if isinstance(user_input,str):
                if not user_input.strip():
                    raise ValueError("用户输入内容不能为纯空格")
            if not system_prompt:
                system_prompt = "你是一个AI助手"  # 默认系统提示
            
            messages = [
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": f"输入的文本是：{user_input}"}
            ]
        else:
            if not isinstance(messages, list):
                raise ValueError("messages必须是消息字典列表")
            if len(messages) == 0:
                raise ValueError("messages不能为空列表")
            if any(not isinstance(m, dict) for m in messages):
                raise ValueError("messages中的元素必须是字典")
        
        try:
            response = self._client.chat.completions.create(
                model=self.model_name,
                messages=messages,
                temperature=temperature,
                tools=tools if tools else None
            )
            return response.choices[0].message.content
        except Exception as e:
            raise ValueError(f"API请求失败: {str(e)}")

    def _chat_completion_via_requests(
        self,
        system_prompt: Optional[str] = None,
        user_input: Optional[str] = None,
        messages: Optional[list] = None,
        temperature: float = 0.0,
        tools: Optional[list] = None,
        timeout: float = 30.0,
        stream = False
    ) -> str:
        """
        使用 requests 直接发送 HTTP 请求到 OpenAI 兼容 API

        :param system_prompt: 系统提示词，可选
        :param user_input: 用户输入，可选
        :param messages: 自定义消息结构（优先）
        :param temperature: 温度系数
        :param timeout: 请求超时时间（秒）
        :param tools: 工具调用定义
        :return: 模型生成的响应内容
        :raises: 
            ValueError: 输入为空或格式不正确
            requests.exceptions.RequestException: 网络或 API 错误
        """

        # 参数校验与标准化
        if messages is None:
            if not user_input or (isinstance(user_input, str) and not user_input.strip()):
                raise ValueError("user_input不能为空或纯空格")
            if not system_prompt:
                system_prompt = "你是一个AI助手"  # 默认系统提示
            
            messages = [
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": f"输入的文本是：{user_input}"}
            ]
        else:
            if not isinstance(messages, list):
                raise ValueError("messages必须是消息字典列表")
            if len(messages) == 0:
                raise ValueError("messages不能为空列表")
            if any(not isinstance(m, dict) for m in messages):
                raise ValueError("messages中的元素必须是字典")

        if not self.base_url or not self.api_key:
            raise ValueError("API配置不完整（缺少base_url或api_key）")

        # 构造请求数据
        payload = {
            "model": self.model_name,
            "messages": messages,
            "stream":stream
        }
        print("payload is ",payload)
        if tools:
            payload["tools"] = tools

        headers = {
            "Content-Type": "application/json",
            "token": f"{self.api_key}",
        }

        try:
            response = requests.post(
                url=f"{self.base_url}",
                headers=headers,
                data=json.dumps(payload),
                timeout=timeout
            )
            response.raise_for_status()
            result = response.json()
            print("result is ",result)
            return result['choices'][0]['message']['content']
        except json.JSONDecodeError as e:
            raise ValueError(f"API响应解析失败: {str(e)}")
        except requests.exceptions.Timeout:
            raise TimeoutError(f"请求超时（{timeout}秒未响应）")
        except requests.exceptions.RequestException as e:
            raise ConnectionError(f"API请求失败: {str(e)}")


class LlmaindexDeepseekClientWrapper:
    def __init__(
        self,
        base_url: Optional[str] = None,
        api_key: Optional[str] = None,
        model_name: Optional[str] = None
    ):
        """
        初始化LLM客户端，自动调用 update_settings 完成初始化
        """
        self.base_url = None
        self.api_key = None
        self.model_name = None
        self._client = None
        self.model_getway = "openai"
        self.update_settings(base_url, api_key, model_name)  # 通过update_settings统一初始化

    def update_settings(
        self,
        base_url: Optional[str] = None,
        api_key: Optional[str] = None,
        model_name: Optional[str] = None
    ) -> None:
        """更新连接设置并重建客户端"""
        self.base_url = base_url or os.environ['LLM_API_URL']
        self.api_key = api_key or os.environ['LLM_API_KEY']
        self.model_name = model_name or os.environ['LLM_MODEL_NAME']
        self._client = DeepSeek(model=self.model_name, api_key=self.api_key)  # 重建客户端
        self.model_getway = os.environ['API_MODEL_GETWAY']

        logging.info("模型客户端初始化完成，配置信息如下：")
        logging.info(f" - API URL       : {self.base_url}")
        logging.info(f" - 模型名称       : {self.model_name}")
        logging.info(f" - 发送请求方式       : {self.model_getway}")
    def get_client(self):
        """获取DeepSeek客户端实例"""
        if self._client is None:
            raise ValueError("客户端未初始化，请先调用 update_settings()")
        return self._client
    
def test_llm():
    # 初始化（使用默认参数）
    client = OpenAIClientWrapper()
    # ✅ 用例1：传入 messages（方式一）
    try:
        print("\n【用例1】使用 messages（问天气）")
        messages = [
            {"role": "system", "content": "你是一位天气助手"},
            {"role": "user", "content": "请告诉我今天北京的天气"}
        ]
        response = client.chat_completion(messages=messages)
        print("响应:", response)
    except Exception as e:
        print("用例1 出错:", type(e).__name__, e)

    # ✅ 用例2：传入 system_prompt + user_input（方式二）
    try:
        print("\n【用例2】使用 prompt + input（翻译助手）")
        response = client.chat_completion(
            system_prompt="你是一位中英翻译助手",
            user_input="请将下面句子翻译为英文：我喜欢使用人工智能工具。"
        )
        print("响应:", response)
    except Exception as e:
        print("用例2 出错:", type(e).__name__, e)

    # ✅ 用例3：传入 messages（方式一） - 多轮对话
    try:
        print("\n【用例3】使用 messages（多轮对话模拟）")
        messages = [
            {"role": "system", "content": "你是一个幽默但专业的AI助手"},
            {"role": "user", "content": "你好"},
            {"role": "assistant", "content": "你好！今天有什么我可以帮忙的吗？"},
            {"role": "user", "content": "能讲个笑话吗？"}
        ]
        response = client.chat_completion(messages=messages)
        print("响应:", response)
    except Exception as e:
        print("用例3 出错:", type(e).__name__, e)

    # ✅ 用例4：传入 system_prompt + user_input（方式二） - 简单问答
    try:
        print("\n【用例4】使用 prompt + input（介绍牛顿）")
        response = client.chat_completion(
            system_prompt="你是一个科学知识问答助手",
            user_input="请简要介绍一下牛顿的三大定律"
        )
        print("响应:", response)
    except Exception as e:
        print("用例4 出错:", type(e).__name__, e)

# 使用示例（演示update_settings的调用）
if __name__ == "__main__":
    test_llm()